/*
 * Bucket.cpp
 *
 *  Created on: 30/10/2011
 *      Author: damnvb
 */

#include <sstream>
#include <fstream>
#include <iostream>
#include <cstring>
#include "stdio.h"
#include "stdlib.h"
#include "string.h"

#include "Bucket.h"
#include "Registro.h"
#include "Util.h"

unsigned int Bucket::TAMANIO_BUCKET = 1024;
int Bucket::REG_NO_EN_BUCKET = -1;
int Bucket::REG_NO_ENTRA = -2;
int Bucket::REG_MAYOR_QUE_BUCKET = -3;
int Bucket::RES_OK = -4;
int Bucket::RES_ERROR = -5;

Bucket::Bucket(unsigned int tamanioDispersion) {
	this->cantidadRegistros = 0;
	this->setEspacioLibre(TAMANIO_BUCKET-8);
	this->setTamanioDispersion(tamanioDispersion);
}

Bucket::Bucket(string path,int nroBloque) {

	fstream archivo;
	string datosLeidos;
	this->espacioLibre = this->TAMANIO_BUCKET;
	this->cantidadRegistros = 0;

	archivo.open(path.c_str(), fstream::in | fstream::out | fstream::binary);

    if (archivo.is_open()){

    	string datosLeidos;
	 	datosLeidos.resize(TAMANIO_BUCKET);
	 	archivo.seekg(nroBloque*TAMANIO_BUCKET, ios_base::beg);
	 	archivo.read(&(datosLeidos[0]), datosLeidos.size());

	 	this->deserializar(datosLeidos);
	    archivo.close();
    }else {
    	this->cantidadRegistros = 0;
    	this->setEspacioLibre(TAMANIO_BUCKET-8);
    	this->setTamanioDispersion(1);
    }


}

int Bucket::insertar(Registro* reg) {
	this->borrar(reg);
	if (espacioLibre <= reg->size()) {
		if (reg->size() > this->TAMANIO_BUCKET){
			this->borrar(reg);
			cout << "Pasado a stopword: " << reg->getKey() << endl;
			return REG_MAYOR_QUE_BUCKET;
		}
		return REG_NO_ENTRA;
	}
	else {
		this->setEspacioLibre(espacioLibre - reg->size());
		Registro *reg_aux = new Registro(reg);
		this->memoria.push_back(reg_aux);
		this->cantidadRegistros++;
	}

	return RES_OK;
}

int Bucket::buscar(Registro* reg) {
	string keyBuscada = reg->getKey();
	unsigned int i = 0;
	bool encontrado = false;
	while((i < memoria.size()) && (!encontrado))
	{
		encontrado = (keyBuscada == memoria[i]->getKey());
		i++;
	}
	if (encontrado) {
		reg->setValues(memoria[i-1]->valores);
		return RES_OK;
	}
	else return REG_NO_EN_BUCKET;
}

int Bucket::borrar(Registro* reg) {
	string keyBuscada = reg->getKey();
	int encontrado = REG_NO_EN_BUCKET;
	for (unsigned int i = 0; i < memoria.size(); i++)
		if ((keyBuscada == memoria[i]->getKey()))
		{
			this->setEspacioLibre(this->getEspacioLibre()+memoria[i]->size());
			delete memoria[i];
			memoria.erase(memoria.begin()+i);
			encontrado = RES_OK;
		}
	this->cantidadRegistros = memoria.size();

	return encontrado;
}

vector<Registro*> Bucket::vaciar() {
	vector<Registro*> result = this->memoria;
	this->memoria.erase(memoria.begin(),memoria.end());
	return result;
}

int Bucket::persistir(string path, int nroBloque) {

	fstream arc;
	arc.open(path.c_str(), fstream::in | fstream::out | fstream::binary);

	if (!(arc.is_open())) arc.open(path.c_str(), ios::in | ios::out | ios::binary | ios::trunc );

	string datos = this->serializar();
    arc.seekp(nroBloque*TAMANIO_BUCKET, ios_base::beg);
    arc.write(datos.c_str(), TAMANIO_BUCKET);
    arc.close();

    if (arc.good()) {
            return RES_OK;
    } else {
            return RES_ERROR;
    }
}

void Bucket::deserializar(string datos) {
	this->setTamanioDispersion(Util::stringToInt(datos.substr(0,4)));
	vector<Registro*> newMemoria;
	memoria = newMemoria;
	espacioLibre = this->TAMANIO_BUCKET;
	cantidadRegistros = 0;

	Registro *myReg;
	unsigned int cantRegs = Util::stringToInt(datos.substr(4,4));
	int offsetEnBloque = 0;
	for (unsigned int i = 0; i < cantRegs; i++) {
		unsigned int lgthKey = Util::stringToInt(datos.substr(8 + offsetEnBloque,4));
		string key = datos.substr(12 + offsetEnBloque,lgthKey);
		myReg = new Registro(key);

		vector<par_libro> values;
		unsigned int cantRefs = Util::stringToInt(datos.substr(12+lgthKey + offsetEnBloque,4));

		for (unsigned int j = 0; j < cantRefs; j++) {
			par_libro myPar;
			myPar.ID_libro 			= Util::stringToInt(datos.substr(12+lgthKey+4+(j*8)+ offsetEnBloque,4));
			myPar.cant_repeticiones = Util::stringToInt(datos.substr(12+lgthKey+4+4+(j*8)+ offsetEnBloque, 4));
			values.push_back(myPar);
		}
		myReg->setValues(values);
		this->insertar(myReg);

		delete myReg;

		offsetEnBloque = offsetEnBloque + 4 + lgthKey + 4 + (cantRefs * 8);
	}
}

string Bucket::serializar() {
	string result = Util::IntToString(this->tamanioDispersion);
	result = result + Util::IntToString(memoria.size());
	for (unsigned int i = 0; i < memoria.size(); i++) {
		string key = memoria[i]->getKey();
		vector<par_libro> values = memoria[i]->getValues();
		result = result + Util::IntToString(key.length());
		result = result + key;
		result = result + Util::IntToString(values.size());
		for (unsigned int j = 0; j < values.size(); j++) {
			result = result + Util::IntToString(values[j].ID_libro);
			result = result + Util::IntToString(values[j].cant_repeticiones);
		}
	}
	while (result.size() < this->TAMANIO_BUCKET)
		result = result + "-";
	return result;
}

void Bucket::setEspacioLibre(unsigned int cant) {
	this->espacioLibre = cant;
}

unsigned int Bucket::getEspacioLibre() {
	return this->espacioLibre;
}

void Bucket::setTamanioDispersion(unsigned int tamanioDispersion) {
	this->tamanioDispersion = tamanioDispersion;
}

unsigned int Bucket::getTamanioDispersion() {
	return this->tamanioDispersion;
}

unsigned int Bucket::getCantidadRegistros() {
	return this->memoria.size();
}

vector<Registro*> Bucket::getMemoria() {
	return this->memoria;
}

Bucket::~Bucket() {
	for (unsigned int i = 0; i < memoria.size(); i++)
		delete memoria[i];
}

